import java.applet.Applet;
import java.applet.AudioClip;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.*;

import com.googlecode.javacv.CanvasFrame;
import com.googlecode.javacv.FrameGrabber;
import com.googlecode.javacv.OpenCVFrameGrabber;
import com.googlecode.javacv.VideoInputFrameGrabber;
import com.googlecode.javacv.cpp.opencv_core.IplImage;

import static com.googlecode.javacv.cpp.opencv_core.*;
import static com.googlecode.javacv.cpp.opencv_imgproc.*;
import static com.googlecode.javacv.cpp.opencv_objdetect.*;
import static com.googlecode.javacv.cpp.opencv_highgui.*;
import static com.googlecode.javacv.cpp.opencv_core.cvFlip;

public class ColorApplet extends Applet implements Runnable {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private FrameGrabber grabber = null;
	private IplImage grabbedImage = null;
	private boolean stop = false;
	private Exception exception = null;
	Font fontS = new Font("TimesRoman", Font.PLAIN, 20);
	Font fontB = new Font("TimesRoman", Font.PLAIN, 50);
	Font fontOver = new Font("Arial", Font.ITALIC, 100);
	int width, height;
	int gposX = 0, gposY = 0;
	int rposX = 0, rposY = 0;
	int bposX = 0, bposY = 0;
	Point redPoint = new Point(0, 0);
	Point greenPoint = new Point(0, 0);
	Point bluePoint = new Point(0, 0);
	// Stroke st = new BasicStroke(1, BasicStroke.CAP_BUTT,
	// BasicStroke.JOIN_BEVEL,0, new float[]{16,4},0);
	Stroke st = new BasicStroke(5.0F);
	boolean beat = false;
	boolean gameStarted = false;
	int score = 0;

	Rectangle rectStart = new Rectangle(210, 175, 243, 110);
	Rectangle rect1 = new Rectangle(40, 160, 140, 125);
	Rectangle rect2 = new Rectangle(210, 125, 85, 90);
	Rectangle rect3 = new Rectangle(325, 120, 100, 110);
	Rectangle rect4 = new Rectangle(460, 125, 150, 160);
	Rectangle rect5 = new Rectangle(175, 325, 100, 100);
	Rectangle rect6 = new Rectangle(375, 320, 100, 100);

	static CvScalar greenColor_min = cvScalar(0, 90, 0, 0);
	static CvScalar greenColor_max = cvScalar(80, 255, 80, 0);
	static CvScalar redColor_min = cvScalar(0, 0, 90, 0);
	static CvScalar redColor_max = cvScalar(80, 80, 255, 0);
	static CvScalar blueColor_min = cvScalar(90, 0, 0, 0);
	static CvScalar blueColor_max = cvScalar(255, 80, 80, 0);
	CanvasFrame canvas = new CanvasFrame("DrumSet Canvas");
	JPanel jp = new JPanel();
	
	Sound cymbalSound = new Sound("sound/cymbalSound.wav");
	Sound cymbal2Sound = new Sound("sound/cymbal2Sound.wav");
	Sound drumLowerSound = new Sound("sound/drum1.wav");
	Sound drumLeftSound = new Sound("sound/drum4.wav");
	Sound drumRightSound = new Sound("sound/drum2.wav");
	Sound drum2Sound = new Sound("sound/drum3.wav");
	Sound drum3Sound = new Sound("sound/drum5.wav");
	Sound game1Music = new Sound("sound/game1.wav");
	
	//Sound game1Music = new Sound("sound/game2.wav");
	MusicNotes music1 = new MusicNotes();
	//Music2 music1 = new Music2();
	public ColorApplet() {
		jp.setPreferredSize(new Dimension(640, 480));
		canvas.setContentPane(jp);
	}	
	@Override
	public void init() {
		width = 640;// getWidth();
		height = 480;// getHeight();
	}
	@Override
	public void start() {
		try {
			new Thread(this).start();
		} catch (Exception e) {
			if (exception == null) {
				exception = e;
				repaint();
			}
		}
	}
	@Override
	public void run() {
		IplImage img;
		try {
			try {
				grabber = FrameGrabber.createDefault(0);
				grabber.setImageWidth(640);// getWidth());
				grabber.setImageHeight(480);// getHeight());
				grabber.start();
				img = grabber.grab();
			} catch (Exception e) {
				if (grabber != null)
					grabber.release();
				grabber = new OpenCVFrameGrabber(0);
				grabber.setImageWidth(640);// getWidth());
				grabber.setImageHeight(480);// getHeight());
				grabber.start();
				img = grabber.grab();
			}
			while (!stop) {
				img = grabber.grab();
				if (img != null) {
					// show image on window
					cvFlip(img, img, 1);
					canvas.showImage(img);
					grabbedImage = img.clone();
					IplImage greenImage = getGreenThreshold(img);
					IplImage redImage = getRedThreshold(img);
					IplImage blueImage = getBlueThreshold(img);
					CvMoments gmoments = new CvMoments();
					CvMoments rmoments = new CvMoments();
					CvMoments bmoments = new CvMoments();
					cvMoments(greenImage, gmoments, 1);
					cvMoments(redImage, rmoments, 1);
					cvMoments(blueImage, bmoments, 1);
					// blue
					double bmom10 = cvGetSpatialMoment(bmoments, 1, 0);
					double bmom01 = cvGetSpatialMoment(bmoments, 0, 1);
					double barea = cvGetCentralMoment(bmoments, 0, 0);
					bposX = (int) (bmom10 / barea);
					bposY = (int) (bmom01 / barea);
					bluePoint.x = bposX;
					bluePoint.y = bposY;
					// green
					double gmom10 = cvGetSpatialMoment(gmoments, 1, 0);
					double gmom01 = cvGetSpatialMoment(gmoments, 0, 1);
					double garea = cvGetCentralMoment(gmoments, 0, 0);
					gposX = (int) (gmom10 / garea);
					gposY = (int) (gmom01 / garea);
					greenPoint.x = gposX;
					greenPoint.y = gposY;
					// red
					double rmom10 = cvGetSpatialMoment(rmoments, 1, 0);
					double rmom01 = cvGetSpatialMoment(rmoments, 0, 1);
					double rarea = cvGetCentralMoment(rmoments, 0, 0);
					rposX = (int) (rmom10 / rarea);
					rposY = (int) (rmom01 / rarea);
					redPoint.x = rposX;
					redPoint.y = rposY;
					repaint();
					paintCanvas(rposX, rposY, gposX, gposY, bposX, bposY);
				}
			}
			img = null;
			grabber.stop();
			grabber.release();
			grabber = null;
		} catch (Exception e) {
			if (exception == null) {
				exception = e;
				repaint();
			}
		}
	}
	@Override
	public void update(Graphics g) {
		paint(g);
	}	
	
	public void paintCanvas(int rposX, int rposY, int gposX, int gposY,
			int bposX, int bposY) {
		BufferedImage bgBufferedImage = null;
		try {
			bgBufferedImage = ImageIO.read(this.getClass().getResource("img/drumkit.jpg"));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		Graphics g = jp.getGraphics();
		Graphics2D g2 = bgBufferedImage.createGraphics();
		
		if (rposX > 0 && rposY > 0) {
			g2.setColor(Color.red);
			g2.fillOval(rposX - 25, rposY - 25, 50, 50);
		}
		if (gposX > 0 && gposY > 0) {
			g2.setColor(Color.green);
			g2.fillOval(gposX - 25, gposY - 25, 50, 50);
		}
		if (bposX > 0 && bposY > 0) {
			g2.setColor(Color.blue);
			g2.fillOval(bposX - 25, bposY - 25, 50, 50);
		}
		g.drawImage(bgBufferedImage, 0, 0, null);
	}

	boolean gamePlaying = false;
	double startTime = 0;

	public void paint(Graphics g) {

		if (!gameStarted) {
			initPaint(g);
		}
		if (gameStarted && !gameOver) {
			if (!gameOver) {
				gameStart(g);
				if (!gamePlaying) {
					game1Music.play();
					startTime = getTime();
				}
				gamePlaying = true;
			} 
		}
		else if(gameOver){
			gameOverPaint(g);
		}

	}

	public void initPaint(Graphics g) {		
		BufferedImage initBufferedImage = null;
		try {
			initBufferedImage = ImageIO.read(this.getClass().getResource("img/initBg.jpg"));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		Graphics2D g2 = initBufferedImage.createGraphics();
		  //control by red 
		if (rposX > 0 && rposY > 0) {
		  g2.setColor(Color.red); 
		  g2.fillOval(rposX - 25, rposY - 25, 50, 50);
		}
		// control by green
		if (gposX > 0 && gposY > 0) {
			g2.setColor(Color.green);
			g2.fillOval(gposX - 25, gposY - 25, 50, 50);
		}
		g.drawImage(initBufferedImage, 0, 0, null);
		if (rectStart.contains(redPoint)) {
			gameStarted = true;
		}
	}

	public void gameOverPaint(Graphics g) {
		BufferedImage initBufferedImage = null;
		try {
			initBufferedImage = ImageIO.read(this.getClass().getResource("img/gameOver.jpg"));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		g.drawImage(initBufferedImage, 0, 0, 640, 480, null);
		g.setFont(fontS);
		g.setColor(Color.white);
		g.drawString("Score: " + score + "/ " + music1.notesN, 20, 50);
		stop();
	}

	double last = 0;
	double timePointer = 0;
	int i = 0;
	int d = 20;
	int x = 0;
	int y = 0;
	int totalTime = music1.totalTime;
	int timeLeft = 0;
	boolean gameOver = false;
	boolean showed = false;
	
	public void gameStart(Graphics g) {

		if (grabbedImage != null) {
			BufferedImage image = grabbedImage.getBufferedImage(2.2 / grabber
					.getGamma());
			Graphics2D g2 = image.createGraphics();
			// ///////////////////////////////////////////////////
			g2.setColor(Color.orange);
			g2.setFont(fontS);
			g2.drawString("Instrument 1", 40, 20);
			g2.drawString("Instrument 2", 185, 20);
			g2.drawString("Instrument 3", 325, 20);
			g2.drawString("Instrument 4", 480, 20);
			g2.drawString("Instrument 5", 175, 80);
			g2.drawString("Instrument 6", 375, 80);
			g2.setFont(fontB);
			g2.drawString("Score: " + score + "/ " + music1.notesN, 10, 470);
			timeLeft = totalTime - (int) timePointer;
			if (timeLeft == 0) {
				gameOver = true;
			}
			g2.drawString("Time: " + timeLeft, 400, 470);
			timePointer = getTime() - startTime;
			
			if (timePointer >= music1.timeLine[i] - 1
					&& timePointer <= music1.timeLine[i]) {
				x = music1.points[music1.notes[i]][0] + 40;
				y = music1.points[music1.notes[i]][1] - 10;
				if (timePointer < music1.timeLine[i] - 0.5
						&& timePointer > music1.timeLine[i] - 1) {
					g2.drawOval(x, y, d, d);
				} else if (timePointer > music1.timeLine[i] - 0.5
						&& timePointer < music1.timeLine[i]) {
					g2.fillOval(x, y, d, d);
					g2.setFont(fontB);
					if (instrumentN == music1.notes[i] && !showed) {
						g2.drawString("+1", 10, 100);
						score++;
						instrumentN = -1;
						showed = true;
					} else if(instrumentN != music1.notes[i] && !showed){
						g2.drawString("missed", 10, 100);
						showed = true;
					}
				}
			}
			if (timePointer >= music1.timeLine[i] && i < music1.notesN - 1){
				i++;
				showed = false;
			}
			// //////////////////////////////////////////////////
			g2.setColor(Color.gray);
			g2.setStroke(st);
			g2.drawRect(40, 160, 140, 125);
			g2.drawRect(210, 125, 85, 90);
			g2.drawRect(325, 120, 100, 110);
			g2.drawRect(460, 125, 150, 160);
			g2.drawRect(175, 325, 100, 100);
			g2.drawRect(375, 320, 100, 100);
			if (gposX > 0 && gposY > 0) {
				g2.setColor(Color.green);
				g2.fillOval(gposX - 25, gposY - 25, 50, 50);
			}
			if (rposX > 0 && rposY > 0) {
				g2.setColor(Color.red);
				g2.fillOval(rposX - 25, rposY - 25, 50, 50);
			}
			if (bposX > 0 && bposY > 0) {
				g2.setColor(Color.blue);
				g2.fillOval(bposX - 25, bposY - 25, 50, 50);
			}
			g.drawImage(image, 0, 0, null);
			beatDrumSet(redPoint);
		}
	}

	int instrumentN = -1;

	private void beatDrumSet(Point point) {
		if (!rect1.contains(point) && !rect2.contains(point)
				&& !rect3.contains(point) && !rect4.contains(point)
				&& !rect5.contains(point) && !rect6.contains(point)) {
			beat = false;
			instrumentN = -1;
		}
		if (!beat) {
			if (rect1.contains(point)) {
				cymbalSound.play();
				instrumentN = 1;
				beat = true;
			} else if (rect2.contains(point)) {
				drumLeftSound.play();
				instrumentN = 2;
				beat = true;
			} else if (rect3.contains(point)) {
				drumRightSound.play();
				instrumentN = 3;
				beat = true;
			} else if (rect4.contains(point)) {
				cymbal2Sound.play();
				instrumentN = 4;
				beat = true;
			} else if (rect5.contains(point)) {
				drum2Sound.play();
				instrumentN = 5;
				beat = true;
			} else if (rect6.contains(point)) {
				drum3Sound.play();
				instrumentN = 6;
				beat = true;
			}
		}
	}

	/*
	 * beat drumSet persistently private void beatDrumSet(Point point){ if
	 * (!beat) { if (rect1.contains(point)) { cymbalSound.play(); beat = true; }
	 * else if (rect2.contains(point)) { drumLeftSound.play(); beat = true; }
	 * else if (rect3.contains(point)) { drumRightSound.play(); beat = true; }
	 * else if (rect4.contains(point)) { cymbal2Sound.play(); beat = true; }
	 * else if (rect5.contains(point)) { drum2Sound.play(); beat = true; } else
	 * if (rect6.contains(point)) { drum3Sound.play(); beat = true; } beat =
	 * false; } }
	 */

	private double getTime() {
		return System.currentTimeMillis() / 1000.0;
	}

	private IplImage getGreenThreshold(IplImage video) {
		IplImage imgThreshold = cvCreateImage(cvGetSize(video), 8, 1);
		cvInRangeS(video, greenColor_min, greenColor_max, imgThreshold);// green
		cvSmooth(imgThreshold, imgThreshold, CV_MEDIAN, 15);
		return imgThreshold;
	}

	private IplImage getRedThreshold(IplImage video) {
		IplImage imgThreshold = cvCreateImage(cvGetSize(video), 8, 1);
		cvInRangeS(video, redColor_min, redColor_max, imgThreshold);// red
		cvSmooth(imgThreshold, imgThreshold, CV_MEDIAN, 15);
		return imgThreshold;
	}

	private IplImage getBlueThreshold(IplImage video) {
		IplImage imgThreshold = cvCreateImage(cvGetSize(video), 8, 1);
		cvInRangeS(video, blueColor_min, blueColor_max, imgThreshold);
		cvSmooth(imgThreshold, imgThreshold, CV_MEDIAN, 15);
		return imgThreshold;
	}

	@Override
	public void stop() {
		stop = true;
		game1Music.stop();
	}

	@Override
	public void destroy() {
	}

}
